//
//  GraphView.m
//  HypERLtrace
//
//  Created by Vladimir Sarpe on 29/07/08.
//  Copyright 2008 __MyCompanyName__. All rights reserved.
///

#import "GraphView.h"


@implementation GraphView

- (void) initPart {
	found = 0;
	overlay = NO;
	location = 0;
	spawn = @"0";
	die = @"0";
	max = 0;
	initdraw = NO;
	dragging = NO;
	backColor = [NSColor colorWithCalibratedWhite:0.8 alpha:1.0];
}

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
		frameRect = [self bounds];
		prevRect = frameRect;
		lastevent = 0;
		graphColor = [NSColor blackColor];
		rawData = [[NSMutableArray alloc] init];
		graphData = [[NSMutableArray alloc] init];
		dataPath = [[NSBezierPath alloc] init];
		overlayPath = nil;
		[self initPart];
	}
    return self;
}

- (NSBezierPath*) drawPath:(NSMutableArray*) data andArgs:(NSString*)args{
	NSBezierPath* path;
	int i;
	int nextx = 0;
	int nexty = 7;
	path = [[NSBezierPath alloc] init];
	[path setLineWidth:1];
	[path moveToPoint:NSMakePoint(nextx, nexty)];
	if ([data count] > 0) {
		for (i = 0; i < [data count]; i++) {
			nextx = i;
			float value = [[[data objectAtIndex:i] objectAtIndex:0] floatValue];
			float logv;
			if (value == 0) {
				logv = 0.0;
			}
			else if (value == 1) {
				logv = 0.5;
			}
			else {
				logv = logf(value);
			}
			float logm = logf(max);
			nexty = 7 + ((logv/logm)*(frameRect.size.height-7));
			[path lineToPoint:NSMakePoint(nextx, nexty)];
		}
	}
	[path lineToPoint:NSMakePoint(nextx, 7)];
	[path lineToPoint:NSMakePoint(frameRect.size.width, 7)];
	[path closePath];
	[self setNeedsDisplay:YES];
	return path;
}

- (void) drawGraph:(NSMutableArray*) data andArgs:(NSString*) args {
	if ([args isEqualToString:@"data"]) {
		dataPath = [self drawPath:data andArgs:args];
	}
	else if ([args isEqualToString:@"overlay"]) {
		overlayPath = [self drawPath:data andArgs:args];
	}
}

- (NSMutableArray*)makeStats:(NSMutableArray*) data {
	NSMutableArray* vals = [[NSMutableArray alloc] init];
	int i;
	int lastev = (frameRect.size.width);
	int numev = (lastevent) / (lastev -1 );
	for (i = 0; i < lastev; i++) {
		NSMutableArray* vals_element = [NSMutableArray arrayWithCapacity:2];
		[vals_element addObject:[NSNumber numberWithInt:0]];
		[vals_element addObject:@"0"];
		[vals addObject:vals_element];
	}
	for (i = 0; i < [data count]; i++) {
		NSString* event = [data objectAtIndex:i];
		int event_pos = [event intValue] / numev;
		int avg = [event intValue];
		NSString* avg_str = [NSString stringWithFormat:@"%d", avg];
		[[vals objectAtIndex:event_pos] replaceObjectAtIndex:1 withObject:avg_str];
		int update = [[[vals objectAtIndex:event_pos] objectAtIndex:0] intValue] + 1;
		[[vals objectAtIndex:event_pos] replaceObjectAtIndex:0 withObject:[NSNumber numberWithInt:update]];
		if (update > max) {
			max = update;
		}
	}
	if (max == 1) {
		max++;
	}
	return vals;
}


- (void)loadData:(NSMutableArray*)data andLast:(int)last andColor:(NSColor*)color andBackground:(NSColor*)bcolor {
	[self initPart];
	backColor = bcolor;
	graphColor = color;
	lastevent = last;
	rawData = data;
	graphData = [self makeStats:data];
}

- (void)drawRect:(NSRect)rect {
	[[self window] setMinSize:NSMakeSize(760.0, 200.0)];
	frameRect = [self bounds];
	if ((initdraw == NO) && (lastevent > 0)) {
		[self drawGraph:graphData andArgs:@"data"];
		initdraw = YES;
	}
	if (![self inLiveResize]) {
		if (prevRect.size.width != frameRect.size.width) {
			[self viewDidEndLiveResize];
		}
	}
	[backColor set];
	NSRectFill(rect);
	if (dragging) {
		NSBezierPath* select = [[NSBezierPath alloc] init];
		[select moveToPoint:NSMakePoint(initloc.x, 0)];
		[select lineToPoint:NSMakePoint(afterloc.x, 0)];
		[select lineToPoint:NSMakePoint(afterloc.x, frameRect.size.height)];
		[select lineToPoint:NSMakePoint(initloc.x, frameRect.size.height)];
		[select closePath];
		[[NSColor colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:0.3] set];
		//[[NSColor colorWithCalibratedWhite:0.8 alpha:1.0] set];
		[select fill];
		NSBezierPath* left = [[NSBezierPath alloc] init];
		[left moveToPoint:NSMakePoint(initloc.x, 0)];
		[left lineToPoint:NSMakePoint(initloc.x, frameRect.size.height)];
		[[NSColor redColor] set];
		[left stroke];
		NSBezierPath* right = [[NSBezierPath alloc] init];
		[right moveToPoint:NSMakePoint(afterloc.x, 0)];
		[right lineToPoint:NSMakePoint(afterloc.x, frameRect.size.height)];
		[[NSColor redColor] set];
		[right stroke];
	}
	[graphColor set];
	[dataPath fill];
	if (overlay) {
		[self drawGraph:graphOverlayData andArgs:@"overlay"];
		[[NSColor redColor] set];
		[overlayPath fill];
	}

	NSBezierPath* base = [[NSBezierPath alloc] init];
	[base setLineWidth:1.0];
	[base moveToPoint:NSMakePoint(0,7)];
	[base lineToPoint:NSMakePoint(frameRect.size.width, 7)];
	[[NSColor blackColor] set];
	[base stroke];
	
	NSBezierPath* loc = [[NSBezierPath alloc] init];
	[loc setLineWidth:1.0];
	[loc moveToPoint:NSMakePoint(location,7)];
	[loc lineToPoint:NSMakePoint(location, frameRect.size.height)];
	[[NSColor redColor] set];
	[loc stroke];
	
	NSBezierPath* lifetime = [[NSBezierPath alloc] init];
	[lifetime setLineWidth:3.0];
	[lifetime moveToPoint:NSMakePoint([spawn intValue], 5)];
	[lifetime lineToPoint:NSMakePoint([die intValue], 5)];
	[[NSColor redColor] set];
	[lifetime stroke];
	
	NSString* stats = [NSString stringWithFormat:@"%d / %d", found, [rawData count]];
	NSMutableAttributedString *s = [[NSMutableAttributedString alloc] initWithString:stats];
	[s addAttribute:NSFontAttributeName	value:[NSFont userFontOfSize:9] range:NSMakeRange(0,[s length])];
	[s addAttribute:NSForegroundColorAttributeName value:[NSColor blackColor] range:NSMakeRange(0,[s length])];
	NSString *last = [stats lastPathComponent];
	int range_l = [stats length] - [last length] - 1;
	[s addAttribute:NSForegroundColorAttributeName value:[NSColor redColor] range:NSMakeRange(0, range_l)];
	NSRect text = NSMakeRect(0, frameRect.size.height-10, 100, 10);
	[s drawInRect:text];
}

- (BOOL)isOpaque { 
    return [[NSColor whiteColor] alphaComponent] >= 1.0 ? YES : NO;
}

- (void) viewDidEndLiveResize {
	graphData = [self makeStats:rawData];
	graphOverlayData = [self makeStats:rawOverlayData];
	[self drawGraph:graphData andArgs:@"data"];
	[self drawGraph:graphOverlayData andArgs:@"overlay"];
	[self setNeedsDisplay:YES];
}

- (void) notifyMatched:(NSMutableArray*) matched andQuery:(BOOL)query {
	found = [matched count];
	NSMutableArray* matchedArray = [NSMutableArray arrayWithCapacity:found];
	if (query) {
		int i;
		for (i = 0; i < found; i++) {
			[matchedArray addObject:[[matched objectAtIndex:i] valueForKey:@"timestamp"]];
		}
		overlay = YES;
		rawOverlayData = matchedArray;
		graphOverlayData = [self makeStats:matchedArray];
	}
	else {
		overlay = NO;
		found = 0;
	}
}

- (void)notifyMouseOverTime:(int)time {
	float rel_pos = ((float)time/(float)lastevent);
	location = rel_pos*frameRect.size.width;
	[self setNeedsDisplay:YES];
}

- (void)notifyMouseOverActor:(NSString*)lifetime {
	int point;
	NSScanner* scanner = [NSScanner scannerWithString:lifetime];
	[scanner scanUpToString:@"-" intoString:&spawn];
	point = ([spawn floatValue] / (float)lastevent) * (float)frameRect.size.width;
	spawn = [NSString stringWithFormat:@"%d", (int)point];
	if ([scanner isAtEnd]) {
		die = [NSString stringWithFormat:@"%d", (int)frameRect.size.width];
	}
	else {
		[scanner setScanLocation:[scanner scanLocation]+1];
		[scanner scanUpToString:@" " intoString:&die];
		point = ([die floatValue] / (float)lastevent) * (float)frameRect.size.width;
		die = [NSString stringWithFormat:@"%d", (int)point];
	}
	[self setNeedsDisplay:YES];
}

- (void)newLocationByX:(float)x {
    afterloc.x=afterloc.x+x;
	if (afterloc.x < 0) {
		afterloc.x = 0;
	}
	if (afterloc.x > frameRect.size.width) {
		afterloc.x = frameRect.size.width;
	}
	[self setNeedsDisplay:YES];
}

-(void)mouseUp:(NSEvent *)event {
	backColor = [NSColor colorWithCalibratedWhite:0.8 alpha:1.0];
	NSPoint clickLocation = [self convertPoint:[event locationInWindow] fromView:nil];
	afterloc = clickLocation;
	[self newLocationByX:0.0];
    dragging=NO;
    [[self window] invalidateCursorRectsForView:self];
	[self setNeedsDisplay:YES];
	if ((initloc.x - afterloc.x) < 2){
		initloc.x = initloc.x + 2;
		afterloc.x = afterloc.x - 2;
	}
	else if ((afterloc.x - initloc.x) < 2) {
		initloc.x = initloc.x - 2;
		afterloc.x = afterloc.x + 2;
	}
	int ts_left = ((float)initloc.x / (float)frameRect.size.width)*(float)lastevent;
	int ts_right = ((float)afterloc.x / (float)frameRect.size.width)*(float)lastevent;
	if (ts_left > ts_right) {
		int temp = ts_left;
		ts_left = ts_right;
		ts_right = temp;
	}
	NSString* searchString = [NSString stringWithFormat:@"timestamp:%d-%d", ts_left, ts_right];
	[controller performSearch:searchString]; // ignore the warning below.
	
}

-(void)mouseDown:(NSEvent *)event {
	backColor = [NSColor colorWithCalibratedWhite:0.5 alpha:1.0];
	NSPoint clickLocation = [self convertPoint:[event locationInWindow] fromView:nil];
	initloc = clickLocation;
	afterloc = initloc;
	dragging = YES;
	[self setNeedsDisplay:YES];
	lastDragLocation = clickLocation;
} 

-(void)mouseDragged:(NSEvent *)event {
    if (dragging) {
		NSPoint newDragLocation=[self convertPoint:[event locationInWindow] fromView:nil];
		[self newLocationByX:(newDragLocation.x-lastDragLocation.x)];
		[self autoscroll:event];
		lastDragLocation=newDragLocation;
		[self setNeedsDisplay:YES];
    }
}

@end
